package arg

import (
	"db2s/global"
	"db2s/rec"
	"errors"
	"fmt"
	"github.com/urfave/cli"
	"os"
	"strings"
)

const (
	ToolName    = "db2s"
	ToolAuthor  = "LiangHang"
	ToolEmail   = "<ywlianghang@163.com>"
	ProcessName = "db2s"
	Version     = "3.0.0 tools, Release GA-1"
)

func actionFunc(c *cli.Context) error {
	if c.NArg() == 0 && len(c.FlagNames()) == 0 {
		// 当没有传入任何参数时，显示帮助信息
		return errors.New("input arg parameter not legitimate")
	}
	return nil
}

type InitConfigCmdS struct {
	Object string
	Name   string
}
type InitCmdS struct {
	Mode   string
	Config InitConfigCmdS
}

func initCmd(initS *InitCmdS) cli.Command {
	return cli.Command{
		Name:    "init",
		Aliases: []string{"i"},
		Usage:   "initialize a object",
		Action:  actionFunc,
		Subcommands: []cli.Command{
			{
				Name:    "config",
				Aliases: []string{"config"},
				Usage:   "configure the initialization task",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:        "object,o",
						Usage:       "specify the configuration file object. For example: --object or -o task",
						Required:    true,
						Destination: &initS.Config.Object, //赋值
					},
				},
				Action: actionFunc,
			},
		},
	}
}

type AuthKeyCmdS struct {
	Code string
	Free bool
}
type AuthCmdS struct {
	Mode string
	Key  AuthKeyCmdS
}

func authCmd(authS *AuthCmdS) cli.Command {
	return cli.Command{
		Name:    "auth",
		Aliases: []string{"a"},
		Usage:   "tools auth",
		Action:  actionFunc,
		Subcommands: []cli.Command{
			{
				Name:    "key",
				Aliases: []string{"key"},
				Usage:   "license key authorization",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:        "authCode,ca",                                                //命令名称
						Usage:       "Machine authorization code. For example: --authCode or -ca", //命令说明
						Value:       "",                                                           //默认值
						Destination: &authS.Key.Code,                                              //赋值
					},
					&cli.BoolFlag{
						Name:        "Certification-free,cf", //命令名称
						Usage:       "Machine authorization code. For example: --certification-free or -cf",
						Hidden:      true,            //当为true时则隐藏该参数
						Destination: &authS.Key.Free, //赋值
					},
				},
				Action: actionFunc,
			},
		},
	}
}

type rowsDataSyncCmdS struct {
	Config    string
	Name      string
	Truncate  bool
	Optimizer OptimizerSwitch
	Recovered bool
	Recover   rec.RecoverObject
}

type rowsDataCheckCmdS struct {
	Config    string
	Name      string
	Optimizer OptimizerSwitch
}
type RowsDataCmdS struct {
	Mode  string
	Name  string
	Sync  rowsDataSyncCmdS
	Check rowsDataCheckCmdS
}

func rowsDataCmd(rowsDataS *RowsDataCmdS) cli.Command {
	return cli.Command{
		Name:    "rows data",
		Aliases: []string{"rows"},
		Usage:   "Manipulate rows data, including data migration and verification",
		Action:  actionFunc,
		Subcommands: []cli.Command{
			{
				Name:    "sync",
				Aliases: []string{"sync"},
				Usage:   "Migrate rows data from source to target",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:        "config,f",                                                           //命令名称
						Usage:       "Specifies config file. For example: --config gc.conf or -f gc.conf", //命令说明
						Value:       "",                                                                   //默认值
						Required:    false,
						Destination: &rowsDataS.Sync.Config, //赋值
					},
					&cli.StringFlag{
						Name:        "name,n",
						Usage:       "specify unique name of object. For example: --name or -n task1",
						Required:    true,
						Destination: &rowsDataS.Name, //赋值
					},
					&cli.BoolFlag{
						Name:        "truncate,t",
						Usage:       "Confirm truncate behavior. For example: --truncate or -t", //命令说明
						Required:    false,
						Destination: &rowsDataS.Sync.Truncate, //赋值
					},
					&cli.BoolFlag{
						Name:        "cpuOptimizer,cpu",                                                     //命令名称
						Usage:       "Enable CPU performance analysis. For example: --cpuOptimizer or -cpu", //命令说明
						Required:    false,
						Destination: &rowsDataS.Sync.Optimizer.CpuOptimizer, //赋值
					},
					&cli.BoolFlag{
						Name:        "memoryOptimizer,mem",                                                        //命令名称
						Usage:       "Enable memory performance analysis. For example: --memoryOptimizer or -mem", //命令说明
						Required:    false,
						Destination: &rowsDataS.Sync.Optimizer.MemOptimizer, //赋值
					},
					&cli.BoolFlag{
						Name:        "recover,r",                                               //命令名称
						Usage:       "Enable recover parameter. For example: --recover or -r ", //命令说明
						Required:    false,
						Destination: &rowsDataS.Sync.Recovered, //赋值
					},
					&cli.BoolFlag{
						Name:        "variables,var",                                                         //命令名称
						Usage:       "Enable recover parameter variables. For example: --variables or -var ", //命令说明
						Required:    false,
						Destination: &rowsDataS.Sync.Recover.Variables, //赋值
					},
					&cli.StringFlag{
						Name:        "timestamp,time",                                                    //命令名称
						Usage:       "Enable recover parameter time. For example: --timestamp or -time ", //命令说明
						Required:    false,
						Destination: &rowsDataS.Sync.Recover.TimeStamp, //赋值
					},
				},
				Action: actionFunc,
			},
			{
				Name:    "check",
				Aliases: []string{"check"},
				Usage:   "Verify rows data from source to target",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:        "config,f",                                                           //命令名称
						Usage:       "Specifies config file. For example: --config gc.conf or -f gc.conf", //命令说明
						Value:       "",                                                                   //默认值
						Required:    true,
						Destination: &rowsDataS.Check.Config, //赋值
					},
					&cli.StringFlag{
						Name:        "name,n",
						Usage:       "specify unique name of object. For example: --name or -n task1",
						Required:    true,
						Destination: &rowsDataS.Name, //赋值
					},
					&cli.BoolFlag{
						Name:        "cpuOptimizer,cpu",                                                     //命令名称
						Usage:       "Enable CPU performance analysis. For example: --cpuOptimizer or -cpu", //命令说明
						Destination: &rowsDataS.Check.Optimizer.CpuOptimizer,                                //赋值
					},
					&cli.BoolFlag{
						Name:        "memoryOptimizer,mem",                                                        //命令名称
						Usage:       "Enable memory performance analysis. For example: --memoryOptimizer or -mem", //命令说明
						Destination: &rowsDataS.Check.Optimizer.MemOptimizer,                                      //赋值
					},
				},
				Action: actionFunc,
			},
		},
	}
}

type MetaDataStructCmdS struct {
	Config    string
	Name      string
	Optimizer OptimizerSwitch
}
type MetaDataObjectCmdS struct {
	Config    string
	Name      string
	Optimizer OptimizerSwitch
}
type MetaDataCmdS struct {
	Mode   string
	Name   string
	Struct MetaDataStructCmdS
	Object MetaDataObjectCmdS
}

func MetaDataCmd(metaDataS *MetaDataCmdS) cli.Command {
	return cli.Command{
		Name:    "meta data",
		Aliases: []string{"meta"},
		Usage:   "Manipulate meta data, including data migration and verification",
		Action:  actionFunc,
		Subcommands: []cli.Command{
			{
				Name:    "struct",
				Aliases: []string{"struct"},
				Usage:   "Migrate table meta data from source to target",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:        "config,f",                                                           //命令名称
						Usage:       "Specifies config file. For example: --config gc.conf or -f gc.conf", //命令说明
						Value:       "",                                                                   //默认值
						Required:    false,
						Destination: &metaDataS.Struct.Config, //赋值
					},
					&cli.StringFlag{
						Name:        "name,n",
						Usage:       "specify unique name of object. For example: --name or -n task1",
						Required:    true,
						Destination: &metaDataS.Name, //赋值
					},
					&cli.BoolFlag{
						Name:        "cpuOptimizer,cpu",                                                     //命令名称
						Usage:       "Enable CPU performance analysis. For example: --cpuOptimizer or -cpu", //命令说明
						Destination: &metaDataS.Struct.Optimizer.CpuOptimizer,                               //赋值
					},
					&cli.BoolFlag{
						Name:        "memoryOptimizer,mem",                                                        //命令名称
						Usage:       "Enable memory performance analysis. For example: --memoryOptimizer or -mem", //命令说明
						Destination: &metaDataS.Struct.Optimizer.MemOptimizer,                                     //赋值
					},
				},
				Action: actionFunc,
			},
			{
				Name:    "object",
				Aliases: []string{"object"},
				Usage:   "Verify schema object meta data from source to target",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:        "config,f",                                                           //命令名称
						Usage:       "Specifies config file. For example: --config gc.conf or -f gc.conf", //命令说明
						Value:       "",                                                                   //默认值
						Required:    false,
						Destination: &metaDataS.Object.Config, //赋值
					},
					&cli.StringFlag{
						Name:        "name,n",
						Usage:       "specify unique name of object. For example: --name or -n task1",
						Required:    true,
						Destination: &metaDataS.Name, //赋值
					},
					&cli.BoolFlag{
						Name:        "cpuOptimizer,cpu",                                                     //命令名称
						Usage:       "Enable CPU performance analysis. For example: --cpuOptimizer or -cpu", //命令说明
						Destination: &metaDataS.Object.Optimizer.CpuOptimizer,                               //赋值
					},
					&cli.BoolFlag{
						Name:        "memoryOptimizer,mem",                                                        //命令名称
						Usage:       "Enable memory performance analysis. For example: --memoryOptimizer or -mem", //命令说明
						Destination: &metaDataS.Object.Optimizer.MemOptimizer,                                     //赋值
					},
				},
				Action: actionFunc,
			},
		},
	}
}

type TaskScheduleStartCmdS struct {
	Config    string
	Name      string
	Optimizer OptimizerSwitch
}
type TaskScheduleCmdS struct {
	Mode  string
	Start TaskScheduleStartCmdS
}

func TaskScheduleCmd(taskSchedule *TaskScheduleCmdS) cli.Command {
	return cli.Command{
		Name:    "task schedule",
		Aliases: []string{"task"},
		Usage:   "Generate configuration and check of corresponding modules",
		Action:  actionFunc,
		Subcommands: []cli.Command{
			{
				Name:    "start",
				Aliases: []string{"start"},
				Usage:   "Start a service",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:        "config,f",                                                           //命令名称
						Usage:       "Specifies config file. For example: --config gc.conf or -f gc.conf", //命令说明
						Value:       "",                                                                   //默认值
						Required:    true,
						Destination: &taskSchedule.Start.Config, //赋值
					},
					&cli.StringFlag{
						Name:        "name,n",
						Usage:       "specify unique name of object. For example: --name or -n task1",
						Required:    true,
						Destination: &taskSchedule.Start.Name, //赋值
					},
					&cli.BoolFlag{
						Name:        "cpuOptimizer,cpu",                                                     //命令名称
						Usage:       "Enable CPU performance analysis. For example: --cpuOptimizer or -cpu", //命令说明
						Destination: &taskSchedule.Start.Optimizer.CpuOptimizer,                             //赋值
					},
					&cli.BoolFlag{
						Name:        "memoryOptimizer,mem",                                                        //命令名称
						Usage:       "Enable memory performance analysis. For example: --memoryOptimizer or -mem", //命令说明
						Destination: &taskSchedule.Start.Optimizer.MemOptimizer,                                   //赋值
					},
				},
				Action: actionFunc,
			},
		},
	}
}
func strToLow(s string) string {
	return strings.ToLower(strings.TrimSpace(s))
}
func inputHelp() bool {
	aa := os.Args
	for h := range aa {
		if aa[h] == "--help" || aa[h] == "-h" || aa[h] == "-v" || aa[h] == "--version" {
			return true
		}
	}
	return false
}
func cliHelp() (arg ArgInput) {
	var (
		i = &InitCmdS{}
		a = &AuthCmdS{}
		r = &RowsDataCmdS{}
		m = &MetaDataCmdS{}
		t = &TaskScheduleCmdS{}
	)
	app := cli.NewApp()
	app.Name = ToolName                                                                     //应用名称
	app.Usage = fmt.Sprintf("An opensource table and data checksum tool by %v", ToolAuthor) //应用功能说明
	app.Author = ToolAuthor                                                                 //作者
	app.Email = fmt.Sprintf("%v %v", ToolAuthor, ToolEmail)                                 //邮箱
	app.Version = Version
	app.Commands = []cli.Command{
		initCmd(i), // 传递指针
		authCmd(a),
		rowsDataCmd(r),
		MetaDataCmd(m),
		TaskScheduleCmd(t),
	}
	c := &cli.Context{App: app}
	if err := app.Run(os.Args); err != nil {
		fmt.Println(fmt.Sprintf("-- %v report: %v.\n\n", ToolName, err))
		errStr := strings.TrimSpace(fmt.Sprintf("%v", err))
		if !(strings.HasPrefix(errStr, "Required flag") && strings.HasSuffix(errStr, " not set")) && !inputHelp() {
			_ = cli.ShowAppHelp(c)
		}
		global.OsExist()
	}
	aa := os.Args
	if inputHelp() && len(aa) > 1 {
		global.OsExist()
	}
	if inputHelp() || len(os.Args) == 1 {
		_ = cli.ShowAppHelp(c)
		global.OsExist()
	}
	switch strToLow(aa[1]) {
	case "init", "i":
		switch strToLow(aa[2]) {
		case "config":
			i.Mode = "config"
		}
		arg.InitCmd = *i
		arg.Mode = "init"
	case "auth", "a":
		switch strToLow(aa[2]) {
		case "key":
			a.Mode = "auth"
		}
		arg.AuthCmd = *a
		arg.Mode = "auth"
	case "rows data", "rows":
		switch strToLow(aa[2]) {
		case "sync":
			r.Mode = "sync"
			r.Sync.Name = r.Name
		case "check":
			r.Mode = "check"
			r.Check.Name = r.Name
		}
		arg.RowsDataCmd = *r
		arg.Mode = "rows data"
	case "meta data", "meta":
		switch strToLow(aa[2]) {
		case "struct":
			m.Mode = "struct"
			m.Struct.Name = m.Name
		case "object":
			m.Mode = "object"
			m.Object.Name = m.Name
		}
		arg.MetaDataCmd = *m
		arg.Mode = "meta data"
	case "task schedule", "task":
		switch strToLow(aa[2]) {
		case "start":
			t.Mode = "start"
		}
		arg.TaskScheduleCmd = *t
		arg.Mode = "task schedule"
	}
	return
}
